﻿using Microsoft.Devices;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using TakePhotoTogether.Controller;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;

namespace TakePhotoTogether.Communication
{
    public enum NfcPolicy
    {
        AcceptsWifi = 1,
        OnlyBluetooth = 2
    }

    /// <summary>
    /// Class that manages NFC connections.
    /// Should be accessed only using the singleton method.
    /// To start a connection is necessary first to find 
    /// the pairing peer. 'StartPeerFinder' should be called 
    /// to proceed the searching. After a peer is found, the 
    /// Action 'OnPeerFound' is called. In the end 
    /// 'OnPeerFinderCompleted' is called with the opened socket
    /// as parameter.
    /// </summary>
    public class NfcManager
    {
        private const string DEBUG_TAG = "TakePhotoTogether.Communication.NfcManager => ";

        #region Action to communicate NFC events
        public Action OnPeerFinderStarted = null;
        public Action OnPeerFound = null;
        public Action<StreamSocket> OnPeerFinderCompleted = null;
        public Action OnPeerFailed = null;
        public Action OnPeerCanceled = null;
        public Action OnBluetoothEnabledChange = null;
        #endregion

        private readonly VibrateController vibrateController = VibrateController.Default;
        private readonly TimeSpan vibrateTime = TimeSpan.FromMilliseconds(200d);

        #region Singleton Patterm
        private static NfcManager _instance = null;

        /// <summary> Singleton method </summary>
        public static NfcManager Singleton
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new NfcManager();
                }

                return _instance;
            }
        }
        #endregion

        private bool isPeerFinderStarted = false;
        public bool IsPeerFinderConnected
        {
            get;
            private set;
        }

        private bool mIsBluetoothEnabled = true;
        public bool IsBluetoothEnabled
        {
            get { return mIsBluetoothEnabled; }
            set { mIsBluetoothEnabled = value; }
        }

        private bool peerFinderSemaphore = false;

        private NfcManager()
        {
            IsPeerFinderConnected = false;
        }

        /// <summary>
        /// Method to start peer searching
        /// </summary>
        public async Task StartPeerFinder(NfcPolicy nfcPolicy = NfcPolicy.AcceptsWifi)
        {
            Debug.WriteLine("NfcManager::StartPeerFinder - trying to Start");

            int count = 0;

            while (!isPeerFinderStarted && count < 5)
            {
                await TryingToStartPeerFinder(nfcPolicy);

                await Task.Delay(300);

                count++;
            }

            //await VerifyBluetoothAvailability();

        }

        private async Task VerifyBluetoothAvailability()
        {
            try
            {
                Debug.WriteLine(DEBUG_TAG + "display name: " + PeerFinder.DisplayName);

                //Bluetooth Verify (TurnOn/TurnOff)
                PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

                var peers = await PeerFinder.FindAllPeersAsync();
                mIsBluetoothEnabled = true;
                Debug.WriteLine("Bluetooth is turned on");

            }
            catch (Exception ex)
            {
                if ((uint)ex.HResult == 0x8007048F)
                {
                    Debug.WriteLine("Bluetooth is turned off");
                    mIsBluetoothEnabled = false;
                }
            }
        }

        private async Task TryingToStartPeerFinder(NfcPolicy nfcPolicy = NfcPolicy.AcceptsWifi)
        {
            bool result = false;

            if (!isPeerFinderStarted && !peerFinderSemaphore)
            {
                peerFinderSemaphore = true;

                try
                {
                    // Setting up NFC Policy
                    PeerFinder.AllowBluetooth = false;
                    PeerFinder.AllowWiFiDirect = true;
                    PeerFinder.AllowInfrastructure = false;

                    if (nfcPolicy == NfcPolicy.AcceptsWifi)
                    {
                        PeerFinder.AllowInfrastructure = true;
                        PeerFinder.AllowWiFiDirect = true;
                    }
                    else
                    {
                        PeerFinder.AllowInfrastructure = false;
                        PeerFinder.AllowWiFiDirect = false;
                    }

                    await Task.Delay(500);

                    PeerFinder.TriggeredConnectionStateChanged += PeerFinder_TriggeredConnectionStateChanged;
                    PeerFinder.Start();

                    result = true;
                    Debug.WriteLine("NfcManager::StartPeerFinder - Started");

                    if (OnPeerFinderStarted != null)
                    {
                        OnPeerFinderStarted();
                    }
                }
                catch 
                { 
                }

                if (!result)
                {
                    PeerFinder.TriggeredConnectionStateChanged -= PeerFinder_TriggeredConnectionStateChanged;
                }

                isPeerFinderStarted = result;

                peerFinderSemaphore = false;
            }
        }

        /// <summary>
        /// Stops peer searching
        /// </summary>
        public void StopPeerFinder()
        {
            Debug.WriteLine("NfcManager::StopPeerFinder - Trying to Stop");

            if (isPeerFinderStarted && !peerFinderSemaphore)
            {
                peerFinderSemaphore = true;

                PeerFinder.TriggeredConnectionStateChanged -= PeerFinder_TriggeredConnectionStateChanged;
                PeerFinder.Stop();

                Debug.WriteLine("NfcManager::StopPeerFinder - Stopped");
                isPeerFinderStarted = false;
                IsPeerFinderConnected = false;
                peerFinderSemaphore = false;
            }
        }
        /// <summary>
        /// Method to handle state change of peer connection.
        /// The Actions 'OnPeerFound' and 'OnPeerFinderCompleted' 
        /// are the wrapped manner to interact with such state change
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void PeerFinder_TriggeredConnectionStateChanged(object sender, TriggeredConnectionStateChangedEventArgs args)
        {
            if (!isPeerFinderStarted)
            {
                return;
            }

            var state = args.State;

            //Debug.WriteLine("NfcManager::PeerFinder_TriggeredConnectionStateChanged - triggered with state " + state);

            switch (state)
            {
                case TriggeredConnectState.Canceled:
                    if (OnPeerCanceled != null)
                    {
                        OnPeerCanceled();
                    }
                    break;
                case TriggeredConnectState.Failed:
                    if (OnPeerFailed != null)
                    {
                        OnPeerFailed();
                    }
                    break;
                case TriggeredConnectState.PeerFound:
                    if (OnPeerFound != null)
                    {
                        OnPeerFound();
                    }
                  
                    vibrateController.Start(vibrateTime);
                    
                    break;
                case TriggeredConnectState.Completed:
                    //StopPeerFinder();

                    Debug.WriteLine("NfcManager::PeerFinder_TriggeredConnectionStateChanged -" + PeerFinder.DisplayName);

                    
                    if (MainController.Singleton.NoTap)
                    {
                        new StreamSocketConnection(args.Socket).SendPacket(DataPacketFactory.GetLowBatteryPacket());
                        OnPeerFailed();
                    }
                    else
                    {
                        IsPeerFinderConnected = true;

                        if (OnPeerFinderCompleted != null)
                        {

                            OnPeerFinderCompleted(args.Socket);
                        }

                    }

                    break;
            }
        }
    }
}

